package cn.tedu.luban.order.webapi.utils;

import cn.tedu.luban.commons.exception.LubanServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Service
public class IncUidGenerator {

    private static final String IP = getIp();

    private static final AtomicInteger COUNT = new AtomicInteger(0);

    /**
     * 订单编号生成
     * 12位时间戳 + 当前服务器原子性自增长3位 + 当前服务器ip后三位 + 当前用户最后1位
     *
     * @param userId 用户id
     * @return {@link String}
     * @throws LubanServiceException 业务异常
     */
    public String generate(Long userId) throws LubanServiceException {
        //获取当前时间的毫秒数，并取模得到一个11位的数字
        long start = System.currentTimeMillis() % 100000000000L;
        //获取并递增COUNT的当前值
        int andIncrement = COUNT.getAndIncrement();
        //如果COUNT的值大于等于999，则重置为0
        if (COUNT.get() >= 999) {
            COUNT.set(0);
        }
        //将IP字符串转换为整数
        int ipLastInt = Integer.parseInt(IP);
        //拼接生成订单号
        /*
        String.format是一个用于格式化字符串的方法。
        它允许我们通过指定格式规范来构建字符串，可以将各种类型的数据插入到格式化字符串中。
        语法：String formattedString = String.format(format, arguments);
        参数：
        format：格式化字符串的模板，它定义了插入数据的位置和格式。例如 %d 表示整数，%s 表示字符串等。
        arguments：要插入到格式化字符串中的具体值。可以传入一个或多个参数，按照格式规范的顺序进行替换。
         */
        String orderNo = start + String.format("%03d", andIncrement) +
                String.format("%03d", ipLastInt);
        //将订单号与userId进行取模运算后拼接，得到最终的订单号字符串
        return orderNo + userId % 10;
    }

    private static List<String> getHostAddress() throws SocketException {
        List<String> ipList = new ArrayList<>(5);
        Enumeration<NetworkInterface> interfaces =
                NetworkInterface.getNetworkInterfaces();
        while (interfaces.hasMoreElements()) {
            NetworkInterface ni = interfaces.nextElement();
            Enumeration<InetAddress> allAddress = ni.getInetAddresses();
            while (allAddress.hasMoreElements()) {
                InetAddress address = allAddress.nextElement();
                if (address.isLoopbackAddress()) {
                    // skip the loopback addr
                    continue;
                }
                if (address instanceof Inet6Address) {
                    // skip the IPv6 addr
                    continue;
                }
                String hostAddress = address.getHostAddress();
                ipList.add(hostAddress);
            }
        }
        return ipList;
    }

    private static String getIp() {
        String ip = null;
        try {
            List<String> ipList = getHostAddress();
            // default the first
            ip = (!ipList.isEmpty()) ? ipList.get(0) : "";
        } catch (Exception ex) {
            log.warn("Utils get IP warn", ex);
        }

        if (StringUtils.isEmpty(ip)) {
            return "999";
        }
        return ip.split("\\.")[3];
    }
}
